<template>
  <view class="custom-tree-select">
    <view class="select-search" @click="handleOpen">
      <view>{{ hanldeValue }}</view>
    </view>

    <custom-popup v-model="open">
      <view style="padding: 0px 16px 16px 16px; box-sizing: border-box">
        <uni-easyinput type="text" v-model="searchText" @input="hanldeSearch" />
      </view>
      <view v-if="list.length === 0">
        <custom-empty />
      </view>
      <view v-else class="tree-node" v-for="(item, index) in list" :key="item.data[fieldsName.key]">
        <view class="tree-node-content" :class="{ 'tree-node-selected': item.data[fieldsName.key] === value }">
          <view class="indent" v-for="i in item.indent" :key="i"></view>
          <view
            class="collapse-icon"
            :class="{
              'collapse-icon_disabled': !item.data[fieldsName.children] || item.data[fieldsName.children].length === 0,
              'collapse-icon_expand': list.findIndex(({ parent }) => parent === item.data[fieldsName.key]) !== -1,
            }"
            @click="handleExpand(item, index)"
          />
          <view class="content" @click.stop="handleClick(item)">
            {{ item.data[fieldsName.label] }}
          </view>
        </view>
      </view>
    </custom-popup>
  </view>
</template>

<script>
import CustomEmpty from "./custom-empty.vue";
import customPopup from "./custom-popup.vue";
export default {
  components: { customPopup, CustomEmpty },
  props: {
    value: {
      type: [String, Number],
      default: null,
    },
    placeholder: {
      type: String,
      default: "",
    },
    localdata: {
      type: Array,
      default: () => [],
    },
    fieldsName: {
      type: Object,
      default: () => ({
        key: "key",
        label: "label",
        children: "children",
      }),
    },
    customRender: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      list: [],
      open: false,
      searchText: "",
    };
  },
  computed: {
    hanldeValue() {
      const getData = (values, key, fieldsName) => {
        if (values === void 0 || values === null || values.length === 0) return "";

        if (key === void 0 || key === null) return "";

        for (const item of values) {
          if (`${item[fieldsName.key]}` === `${key}`) return item;

          const data = getData(item[fieldsName.children], key, fieldsName);
          if (data) return data;
        }
        return "";
      };

      return getData(this.localdata, this.value, this.fieldsName)[this.fieldsName.label];
    },
  },
  methods: {
    hanldeEmit(emitName, params) {
      this.$emit(emitName, params);
    },
    handleOpen() {
      this.list = this.makeList(this.localdata);
      this.open = true;
    },
    makeList(valuse, indent = 0, parent = -1) {
      const list = [];
      valuse.forEach((data) => {
        list.push({ data, indent, parent });
      });
      return list;
    },
    getChildrenKeys(values, list = {}) {
      values.forEach((item) => {
        list[`${item[this.fieldsName.key]}`] = true;
        if (item[this.fieldsName.children] && item[this.fieldsName.children].length) {
          this.getChildrenKeys(item[this.fieldsName.children], list);
        }
      });
      return list;
    },
    handleExpand({ data, indent }, index) {
      const key = data[this.fieldsName.key];
      const children = data[this.fieldsName.children];

      // 判断是否存在当前节点的子节点 > 不存在(-1) 添加 >> 存在 删除
      const currentIndex = this.list.findIndex(({ parent }) => parent === key);
      if (currentIndex === -1) {
        if (children && children.length > 0) {
          const items = this.makeList(children, indent + 1, key);
          this.list.splice(index + 1, 0, ...items);
        }
      } else {
        // 获取当前节点以下的所有节点 childrenKeys
        const childrenKeys = this.getChildrenKeys(data[this.fieldsName.children]);
        // 删除当前节点以下的所有节点
        this.list = this.list.filter(({ data }) => !childrenKeys[data[this.fieldsName.key]]);
      }
    },
    handleClick(item) {
      this.open = false;
      this.hanldeEmit("input", item.data[this.fieldsName.key]);
    },
    hanldeSearch(searchText) {
      const onSearchData = (values, label, fieldsName, data = []) => {
        if (values === void 0 || values === null || values.length === 0) return null;
        for (const item of values) {
          if (item[fieldsName.label].includes(label)) {
            data.push(item);
          } else {
            onSearchData(item[fieldsName.children], label, fieldsName, data);
          }
        }
        return data;
      };

      if (searchText === "" || searchText === null) {
        this.list = this.makeList(this.localdata);
      } else {
        const data = onSearchData(this.localdata, searchText, this.fieldsName);
        this.list = this.makeList(data);
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.custom-tree-select {
  width: 100%;
  position: relative;

  .select-search {
    width: 100%;
    height: 35px;
    font-size: 14px;
    position: relative;
    overflow: hidden;
    padding-left: 10px;
    text-align: left;
    color: rgb(51, 51, 51);
    border: 1px solid #dcdfe6;
    border-radius: 4px;
    border-color: rgb(229, 229, 229);
    background-color: rgb(255, 255, 255);
    box-sizing: border-box;
    display: flex;
    align-items: center;
  }

  .select-popup::v-deep .bottom {
    height: 400px;
    padding: 16px 16px;
    box-sizing: border-box;
    overflow: hidden auto;
  }

  .tree-node {
    width: 100%;

    .tree-node-content {
      width: 100%;
      box-sizing: border-box;
      position: relative;
      display: flex;
      flex-direction: row;
      align-items: center;
      box-sizing: border-box;

      .indent {
        width: 8px;
        height: 8px;
      }

      .collapse-icon {
        flex-shrink: 0;
        width: 32px;
        height: 32px;
        margin-right: 4px;
        position: relative;

        &::before {
          content: "";
          height: 8px;
          width: 8px;
          right: calc(50% - 5px);
          top: calc(50% - 5px);
          position: absolute;
          border-width: 2px 2px 0 0;
          border-color: #606266;
          border-style: solid;
          -webkit-transform: scale(0.8) rotate(45deg);
          transform: scale(0.8) rotate(45deg);
          transition: transform 0.3s;
        }
        &_disabled {
          opacity: 0;
        }
        &_expand::before {
          -webkit-transform: scale(0.8) rotate(135deg);
          transform: scale(0.8) rotate(135deg);
        }
      }

      .content {
        width: 100%;
        font-size: 14px;
        padding: 8px 0px;
        box-sizing: border-box;

        &:hover {
          background-color: #f5f7fa;
        }
      }
    }

    .tree-node-selected .content {
      color: #007aff;
    }
  }
}
</style>
